﻿using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.Json.Serialization;
using Newtonsoft.Json;

namespace Devonline.Core;

public static class EnumExtensions
{
    /// <summary>
    /// 获取枚举类型的可显示名字
    /// </summary>
    /// <param name="enum">枚举值</param>
    /// <returns></returns>
    public static TValue? GetAttributeValue<TAttribute, TValue>(this Enum @enum, string attributePropertyName) where TAttribute : Attribute
    {
        var field = @enum.GetType().GetField(@enum.ToString("F"));
        if (field is null)
        {
            return default;
        }

        return field.GetAttributeValue<TAttribute, TValue>(attributePropertyName);
    }
    /// <summary>
    /// 获取枚举类型的可显示名字
    /// </summary>
    /// <param name="enum">枚举值</param>
    /// <returns></returns>
    public static string? GetDisplayName(this Enum @enum)
    {
        var field = @enum.GetType().GetField(@enum.ToString("F"));
        if (field == null)
        {
            return string.Empty;
        }

        var display = field.GetCustomAttribute<DisplayAttribute>();
        if (display != null)
        {
            return display.Name;
        }

        var displayName = field.GetCustomAttribute<DisplayNameAttribute>();
        if (displayName != null)
        {
            return displayName.DisplayName;
        }

        var description = field.GetCustomAttribute<DescriptionAttribute>();
        if (description != null)
        {
            return description.Description;
        }

        return string.Empty;
    }
    /// <summary>
    /// 获取对象设置的 json 
    /// </summary>
    /// <param name="enum">枚举值</param>
    /// <returns></returns>
    public static string? GetJsonPropertyName(this Enum @enum)
    {
        var field = @enum.GetType().GetField(@enum.ToString("F"));
        if (field == null)
        {
            return string.Empty;
        }

        var jsonProperty = field.GetCustomAttribute<JsonPropertyAttribute>();
        if (jsonProperty != null)
        {
            return jsonProperty.PropertyName;
        }

        var jsonPropertyName = field.GetCustomAttribute<JsonPropertyNameAttribute>();
        if (jsonPropertyName != null)
        {
            return jsonPropertyName.Name;
        }

        return @enum.ToString();
    }
    /// <summary>
    /// 获取枚举类型的解释说明
    /// </summary>
    /// <param name="enum"></param>
    /// <returns></returns>
    public static string? GetExplanation(this Enum @enum)
    {
        var field = @enum.GetType().GetField(@enum.ToString("F"));
        if (field is null)
        {
            return null;
        }

        var explanation = field.GetCustomAttribute<ExplanationAttribute>();
        if (explanation is not null)
        {
            return explanation.Value;
        }

        return null;
    }

    /// <summary>
    /// 从枚举的 DisplayName 属性得到当前枚举值
    /// 用于从 excel 文本中反射获取枚举值
    /// </summary>
    /// <typeparam name="T">枚举类型</typeparam>
    /// <param name="value">DisplayName 的值</param>
    /// <returns>枚举值</returns>
    public static T? GetEnumValueByAttributeValue<T, TAttribute, TValue>(this string attributePropertyName, [DisallowNull] TValue value) where T : struct, Enum where TAttribute : Attribute
    {
        var type = typeof(T);
        return Enum.GetValues<T>().FirstOrDefault(x => value.Equals(x.GetAttributeValue<TAttribute, TValue>(attributePropertyName)));
    }
    /// <summary>
    /// 从枚举的 DisplayName 属性得到当前枚举值
    /// 用于从 excel 文本中反射获取枚举值
    /// </summary>
    /// <typeparam name="T">枚举类型</typeparam>
    /// <param name="value">DisplayName 的值</param>
    /// <returns>枚举值</returns>
    public static T GetEnumValueByDisplayName<T>(this string value) where T : struct, Enum
    {
        var type = typeof(T);
        return Enum.GetValues<T>().FirstOrDefault(x => type.GetDisplayName(x.ToString()) == value);
    }
    /// <summary>
    /// 从枚举的 DisplayName 属性得到当前枚举值
    /// 用于从 excel 文本中反射获取枚举值
    /// </summary>
    /// <param name="type">当前枚举</param>
    /// <param name="value">DisplayName 的值</param>
    /// <returns>枚举值</returns>
    public static object? GetEnumValueByDisplayName(this string value, Type type)
    {
        var values = Enum.GetValues(type);
        foreach (var item in values)
        {
            if (((Enum)item).GetDisplayName() == value)
            {
                return item;
            }
        }

        return null;
    }
    /// <summary>
    /// 通过枚举字段获取枚举对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <returns></returns>
    public static T GetEnumValueByJsonName<T>(this string value) where T : struct, Enum
    {
        var type = typeof(T);
        return Enum.GetValues<T>().FirstOrDefault(x => type.GetJsonPropertyName(x.ToString()) == value);
    }

    /// <summary>
    /// 将枚举转换为枚举值和显示值的键值对的默认形式
    /// 键采用 F 格式, 值采用可显示特性值
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static ICollection<KeyValuePair<string, string?>> ToKeyValuePairs<T>() where T : struct, Enum => Enum.GetValues<T>().ToDictionary(key => key.ToString("F"), value => value.GetDisplayName());
    /// <summary>
    /// 将枚举转换为键值可定义的键值对
    /// F 格式将显示枚举的字符串表示值
    /// D 格式将显示枚举的数字表示值
    /// 值格式不传值则默认使用可显示特性描述值
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static ICollection<KeyValuePair<string, string?>> ToKeyValuePairs<T>(string keyFormat = "F", string? valueFormat = null) where T : struct, Enum => Enum.GetValues<T>().ToDictionary(key => key.ToString(keyFormat), value => string.IsNullOrWhiteSpace(valueFormat) ? value.GetDisplayName() : value.ToString(valueFormat));

    /// <summary>
    /// 根据时间的小时范围, 获取对应时辰
    /// </summary>
    /// <param name="dateTime">时间</param>
    /// <returns></returns>
    /// <exception cref="KeyNotFoundException"></exception>
    public static EarthlyBranches GetEarthlyBranches(this DateTime dateTime) => dateTime.Hour switch
    {
        23 or 0 => EarthlyBranches.Zi,
        1 or 2 => EarthlyBranches.Chou,
        3 or 4 => EarthlyBranches.Yin,
        5 or 6 => EarthlyBranches.Mao,
        7 or 8 => EarthlyBranches.Chen,
        9 or 10 => EarthlyBranches.Si,
        11 or 12 => EarthlyBranches.Wu,
        13 or 14 => EarthlyBranches.Wei,
        15 or 16 => EarthlyBranches.Shen,
        17 or 18 => EarthlyBranches.You,
        19 or 20 => EarthlyBranches.Xu,
        21 or 22 => EarthlyBranches.Hai,
        _ => throw new KeyNotFoundException("时辰范围不正确!")
    };

    /// <summary>
    /// 获取梅花易数先天八卦
    /// </summary>
    /// <param name="number">数字数值</param>
    /// <returns></returns>
    public static Trigram GetTrigram(this int number) => number switch
    {
        1 => Trigram.Force,
        2 => Trigram.Open,
        3 => Trigram.Radiance,
        4 => Trigram.Shake,
        5 => Trigram.Ground,
        6 => Trigram.Gorge,
        7 => Trigram.Bound,
        8 => Trigram.Field,
        _ => throw new KeyNotFoundException("数字不正确!")
    };
    /// <summary>
    /// 获取二进制先天八卦
    /// </summary>
    /// <param name="number">数字数值</param>
    /// <returns></returns>
    public static Trigram GetHexTrigram(this int number) => number switch
    {
        0 => Trigram.Field,
        1 => Trigram.Shake,
        2 => Trigram.Gorge,
        3 => Trigram.Open,
        4 => Trigram.Bound,
        5 => Trigram.Radiance,
        6 => Trigram.Ground,
        7 => Trigram.Force,
        _ => throw new KeyNotFoundException("数字不正确!")
    };
    /// <summary>
    /// 根据时间获取先天八卦
    /// </summary>
    /// <param name="dateTime">时间数值</param>
    /// <returns></returns>
    public static Trigram GetTrigram(this DateTime dateTime) => dateTime.GetEarthlyBranches() switch
    {
        EarthlyBranches.Zi or EarthlyBranches.Shen => Trigram.Force,
        EarthlyBranches.Chou or EarthlyBranches.You => Trigram.Open,
        EarthlyBranches.Yin or EarthlyBranches.Xu => Trigram.Radiance,
        EarthlyBranches.Mao or EarthlyBranches.Hai => Trigram.Shake,
        EarthlyBranches.Chen => Trigram.Ground,
        EarthlyBranches.Si => Trigram.Gorge,
        EarthlyBranches.Wu => Trigram.Bound,
        EarthlyBranches.Wei => Trigram.Field,
        _ => throw new KeyNotFoundException("时辰范围不正确!")
    };

    /// <summary>
    /// 根据上下卦确定易经 64 卦
    /// </summary>
    /// <param name="trigram">上下卦</param>
    /// <returns></returns>
    /// <exception cref="KeyNotFoundException"></exception>
    public static Hexagram GetHexagram(this (Trigram up, Trigram down) trigram) => trigram switch
    {
        (Trigram.Force, Trigram.Force) => Hexagram.Force,
        (Trigram.Field, Trigram.Field) => Hexagram.Field,
        (Trigram.Gorge, Trigram.Shake) => Hexagram.Sprouting,
        (Trigram.Bound, Trigram.Gorge) => Hexagram.Enveloping,
        (Trigram.Gorge, Trigram.Force) => Hexagram.Attending,
        (Trigram.Force, Trigram.Gorge) => Hexagram.Arguing,
        (Trigram.Field, Trigram.Gorge) => Hexagram.Leading,
        (Trigram.Gorge, Trigram.Field) => Hexagram.Grouping,
        (Trigram.Ground, Trigram.Force) => Hexagram.SmallAccumulating,
        (Trigram.Force, Trigram.Open) => Hexagram.Treading,
        (Trigram.Field, Trigram.Force) => Hexagram.Prevading,
        (Trigram.Force, Trigram.Field) => Hexagram.Obstruction,
        (Trigram.Force, Trigram.Radiance) => Hexagram.ConcordingPeople,
        (Trigram.Radiance, Trigram.Force) => Hexagram.GreatPossessing,
        (Trigram.Field, Trigram.Bound) => Hexagram.Humbling,
        (Trigram.Shake, Trigram.Field) => Hexagram.ProvidingFor,
        (Trigram.Open, Trigram.Shake) => Hexagram.Following,
        (Trigram.Bound, Trigram.Ground) => Hexagram.Corrupting,
        (Trigram.Field, Trigram.Open) => Hexagram.Nearing,
        (Trigram.Ground, Trigram.Field) => Hexagram.Viewing,
        (Trigram.Radiance, Trigram.Shake) => Hexagram.GnawingBite,
        (Trigram.Bound, Trigram.Radiance) => Hexagram.Adorning,
        (Trigram.Bound, Trigram.Field) => Hexagram.Stripping,
        (Trigram.Field, Trigram.Shake) => Hexagram.Returning,
        (Trigram.Force, Trigram.Shake) => Hexagram.WithoutEmbroiling,
        (Trigram.Bound, Trigram.Force) => Hexagram.GreatAccumulating,
        (Trigram.Bound, Trigram.Shake) => Hexagram.Swallowing,
        (Trigram.Open, Trigram.Ground) => Hexagram.GreatExceeding,
        (Trigram.Gorge, Trigram.Gorge) => Hexagram.Gorge,
        (Trigram.Radiance, Trigram.Radiance) => Hexagram.Radiance,
        (Trigram.Open, Trigram.Bound) => Hexagram.Conjoining,
        (Trigram.Shake, Trigram.Ground) => Hexagram.Persevering,
        (Trigram.Force, Trigram.Bound) => Hexagram.Retiring,
        (Trigram.Shake, Trigram.Force) => Hexagram.GreatInvigorating,
        (Trigram.Radiance, Trigram.Field) => Hexagram.Prospering,
        (Trigram.Field, Trigram.Radiance) => Hexagram.BrightnessHiding,
        (Trigram.Ground, Trigram.Radiance) => Hexagram.DwellingPeople,
        (Trigram.Radiance, Trigram.Open) => Hexagram.Polarising,
        (Trigram.Gorge, Trigram.Bound) => Hexagram.Limping,
        (Trigram.Shake, Trigram.Gorge) => Hexagram.TakingApart,
        (Trigram.Bound, Trigram.Open) => Hexagram.Diminishing,
        (Trigram.Ground, Trigram.Shake) => Hexagram.Augmenting,
        (Trigram.Open, Trigram.Force) => Hexagram.Parting,
        (Trigram.Force, Trigram.Ground) => Hexagram.Coupling,
        (Trigram.Open, Trigram.Field) => Hexagram.Clustering,
        (Trigram.Field, Trigram.Ground) => Hexagram.Ascending,
        (Trigram.Open, Trigram.Gorge) => Hexagram.Confining,
        (Trigram.Gorge, Trigram.Ground) => Hexagram.Welling,
        (Trigram.Open, Trigram.Radiance) => Hexagram.Skinning,
        (Trigram.Radiance, Trigram.Ground) => Hexagram.Holding,
        (Trigram.Shake, Trigram.Shake) => Hexagram.Shake,
        (Trigram.Bound, Trigram.Bound) => Hexagram.Bound,
        (Trigram.Ground, Trigram.Bound) => Hexagram.Infiltrating,
        (Trigram.Shake, Trigram.Open) => Hexagram.ConvertingTheMaiden,
        (Trigram.Shake, Trigram.Radiance) => Hexagram.Abounding,
        (Trigram.Radiance, Trigram.Bound) => Hexagram.Sojourning,
        (Trigram.Ground, Trigram.Ground) => Hexagram.Ground,
        (Trigram.Open, Trigram.Open) => Hexagram.Open,
        (Trigram.Ground, Trigram.Gorge) => Hexagram.Dispersing,
        (Trigram.Gorge, Trigram.Open) => Hexagram.Articulating,
        (Trigram.Ground, Trigram.Open) => Hexagram.CentreConfirming,
        (Trigram.Shake, Trigram.Bound) => Hexagram.SmallExceeding,
        (Trigram.Gorge, Trigram.Radiance) => Hexagram.AlreadyFording,
        (Trigram.Radiance, Trigram.Gorge) => Hexagram.NotYetFording,
        _ => throw new KeyNotFoundException("上下卦组合不正确!")
    };
}